/*
 * Static.h
 *
 *  Created on: 2013. 9. 18.
 *      Author: jjaehuny
 */

#ifndef PROJECTZ_NLOGIC_STATIC_H_
#define PROJECTZ_NLOGIC_STATIC_H_

#include "../Config.h"
#include "../NUtil/Single.h"
#include "../NUtil/Pool.h"
#include "../NUtil/Pool_Exception.h"

#include "Space.h"
#include "Monster.h"
#include "Item.h"
#include "QuestInfo.h"
#include "Party.h"
#include "MailBox.h"
#include "Trade.h"
#include "ServerAddrCCU.h"
#include "Memcached.h"

#include "Battle.h"

#include "Indexer.h"
#include "IndexerNewPartyRoomV2.h"
#include "IndexerPVP.h"
#include "IndexerWorldBoss.h"
#include "IndexerPK.h"

#include "WorldBossRank.h"

//#define CHECK_ITEM_LEAK
//#define CHECK_MONSTER_LEAK

namespace ProjectZ { namespace NLogic {

struct Key
{
	Key() : u16dungeon_tid(0), u8difficulty(0) {}
	Key(uint16_t rType, uint8_t rSubType) : u16dungeon_tid(rType), u8difficulty(rSubType) {}

	uint16_t u16dungeon_tid;
	uint8_t u8difficulty;

	bool operator == (const Key & rSide) const
	{
		if (this->u16dungeon_tid == rSide.u16dungeon_tid && this->u8difficulty == rSide.u8difficulty)
			return true;

		return false;
	}

	bool operator < (const Key & rSide) const
	{
		if (this->u16dungeon_tid < rSide.u16dungeon_tid)
		{
			return true;
		}
		else if (this->u16dungeon_tid == rSide.u16dungeon_tid)
		{
			if (this->u8difficulty < rSide.u8difficulty)
				return true;
		}

		return false;
	}
};

class Static : public NUtil::Single<Static>
{
public:
	typedef std::map<int, Space *>			SPACETREE;

	typedef std::vector<Space *> 			SPACEVECTOR;
	typedef std::map<Key, SPACEVECTOR> 	PARTYROOMTREE;
	typedef std::map<int, SPACEVECTOR>		PARTYROOMTREE_V2;

	typedef std::map<int, Space *> 			WORLDBOSSROOMTREE;
	typedef std::map<int, Space *> 			PKROOMTREE;

public:
	Static()
	{
		//_clsSpacePool = new NUtil::Pool<Space>(20 + 3000);
		_clsSpacePool = new NUtil::Pool_Exception<Space>(MAX_SPACE_POOL);

		for (int i = 1; i <= Config::InstancePtr()->GetResourceDatabase()._maxChannel; ++i)
		{
			Space::Data clsData;
			clsData._spaceType = Space::SpaceType::WORLD;
			clsData._index = i;
			clsData._maxUser = 120;

			Space * pSpace = _clsSpacePool->NEW();
			pSpace->Open(NULL, clsData);

			_clsWorldTree[i] = pSpace;
		}

		_clsMonsterPool = new NUtil::Pool<Monster>(1200 * 8);

		_clsItemPool = new NUtil::Pool<Item>(1200 * 300);

		_clsQuestPool = new NUtil::Pool<QuestInfo::Quest>(1200 * 8);
		_clsPartyPool = new NUtil::Pool<Party>(1200);
		_clsMailPool	= new NUtil::Pool<MailBox::Mail>(1200 * 8);
		_clsTradeItemPool	= new NUtil::Pool<Trade::TradeItem>(1200 * 20);

		_clsServerAddrCCU = new ServerAddrCCU;
		_clsMemcached = new Memcached;

		_pBattlePool = new NUtil::Pool<Battle>(1200);

		_clsIndexerPool = new NUtil::Pool<Indexer>(1000);
		_clsPVPIndexerPool = new NUtil::Pool<IndexerPVP>(MAX_NEW_PVP_ROOM);
		_clsPKIndexerPool = new NUtil::Pool<IndexerPK>(MAX_NEW_PK_ROOM);

		for (int dungeonTid = 1; dungeonTid < 60; dungeonTid++)
		{
			{
				SPACEVECTOR clsSpaceVector;
				_clsPartyRoomTree_V2[dungeonTid] = clsSpaceVector;
			}

			for(int difficult = 0; difficult < 3; difficult++)
			{
				SPACEVECTOR clsSpaceVector;
				_clsPartyRoomTree[Key(dungeonTid, difficult)] = clsSpaceVector;
			}
		}

		_pWorldBossRank 			= new WorldBossRank;
		_pWorldBossIndexerPool 	= new NUtil::Pool<IndexerWorldBoss>(1000);


		_clsIndexerNewPartyRoomV2Pool = new NUtil::Pool<IndexerNewPartyRoomV2>(1000);
	}

	~Static()
	{
		delete _clsIndexerNewPartyRoomV2Pool;

		delete _pWorldBossIndexerPool;
		delete _pWorldBossRank;

		delete _pBattlePool;
		delete _clsPartyPool;

		#ifdef CHECK_MONSTER_LEAK
			// Check monster leak
			for (std::map<Monster*, int>::iterator it = _clsMonsterCounter.begin(); it != _clsMonsterCounter.end(); ++it)
			{
				if (it->second != 0)
				{
					printf("MONSTER leak ptr: %p, count: %d\n", it->first, it->second);
				}
			}
		#endif

		delete _clsMonsterPool;

		for (int i = 1; i <= 20; ++i)
		{
			Space * pSpace = _clsWorldTree[i];
			if (NULL != pSpace)
				_clsSpacePool->DEL(pSpace);
		}
		_clsWorldTree.clear();

		std::for_each(_clsSpaceTree.begin(), _clsSpaceTree.end(), [&](SPACETREE::value_type& n) {
			if (NULL != n.second)
				_clsSpacePool->DEL(n.second);
		} );
		_clsSpaceTree.clear();

		// NEW PARTY ROOM DELETE
		std::for_each(_clsPartyRoomTree.begin(), _clsPartyRoomTree.end(), [&](PARTYROOMTREE::value_type& n) {
			//if (NULL != n.second)
			SPACEVECTOR & spaceVector = n.second;

			std::for_each(spaceVector.begin(), spaceVector.end(), [&](Space * pSpace) {
				_clsSpacePool->DEL(pSpace);
			} );

		} );
		_clsPartyRoomTree.clear();

		// PVP Room 정보 반환
		std::for_each(_clsPVPRoomVector.begin(), _clsPVPRoomVector.end(), [&](Space * pSpace) {
			_clsSpacePool->DEL(pSpace);
		} );
		_clsPVPRoomVector.clear();

		// PK Room 반환
		std::for_each(_clsPKRoomTree.begin(), _clsPKRoomTree.end(), [&](PKROOMTREE::value_type & pair) {
			_clsSpacePool->DEL(pair.second);
		} );
		_clsPKRoomTree.clear();

		delete _clsSpacePool;

		#ifdef CHECK_ITEM_LEAK
			// Check item leak
			for (std::map<Item*, int>::iterator it = _clsItemCounter.begin(); it != _clsItemCounter.end(); ++it)
			{
				if (it->second != 0)
				{
					printf("ITEM leak ptr: %p, count: %d\n", it->first, it->second);
				}
			}
		#endif

		delete _clsItemPool;

		delete _clsQuestPool;

		delete _clsMailPool;

		delete _clsTradeItemPool;

		delete _clsServerAddrCCU;

		delete _clsMemcached;

		delete _clsIndexerPool;

		delete _clsPVPIndexerPool;
	}

	Item* NEW_ITEM()
	{
		Item* pItem = _clsItemPool->NEW();
		#ifdef CHECK_ITEM_LEAK
			_clsItemCounter[pItem]++;
		#endif
		return pItem;
	}

	void DEL_ITEM(Item* pItem)
	{
		_clsItemPool->DEL(pItem);
		#ifdef CHECK_ITEM_LEAK
			_clsItemCounter[pItem]--;
		#endif
	}

	Monster* NEW_MONSTER()
	{
		Monster* pMonster = _clsMonsterPool->NEW();
		#ifdef CHECK_MONSTER_LEAK
			_clsMonsterCounter[pMonster]++;
		#endif
		return pMonster;
	}

	void DEL_MONSTER(Monster * pMonster)
	{
		_clsMonsterPool->DEL(pMonster);
		#ifdef CHECK_MONSTER_LEAK
			if ((--_clsMonsterCounter[pMonster]) < 0)
				printf("DEL_MONSTER incorrect counter: %d\n", _clsMonsterCounter[pMonster]);
		#endif
	}

#if 0
	void SendAllSpace(::Network::Packet * pPacket)
	{
		for (SPACETREE::const_iterator citr = _clsSpaceTree.begin(); citr != _clsSpaceTree.end(); ++citr)
		{
			const Space::USERTREE& userTree = citr->second->GetUserTree();
			for (Space::USERTREE::const_iterator citr2 = userTree.begin(); citr2 != userTree.end(); ++citr2)
			{
				citr2->second->SendOneshotEncrypt( pPacket );
			}
		}

		for (SPACETREE::const_iterator citr = _clsWorldTree.begin(); citr != _clsWorldTree.end(); ++citr)
		{
			const Space::USERTREE& userTree = citr->second->GetUserTree();
			for (Space::USERTREE::const_iterator citr2 = userTree.begin(); citr2 != userTree.end(); ++citr2)
			{
				citr2->second->SendOneshotEncrypt( pPacket );
			}
		}

		::Network::GETPACKETPOOL()->SEND_DEL( pPacket );
	}
#endif

	NUtil::Pool_Exception<Space> * GETSPACEPOOL() { return _clsSpacePool; }
	SPACETREE &			GETSPACETREE() { return _clsSpaceTree; }
	SPACETREE &			GETWORLDSPACETREE() { return _clsWorldTree; }
//	NUtil::Pool<Monster> *	GETMONSTERPOOL() { return _clsMonsterPool; }
	NUtil::Pool<QuestInfo::Quest> *	GETQUESTPOOL() { return _clsQuestPool; }
	NUtil::Pool<Party> *		GETPARTYPOOL() { return _clsPartyPool; }
	NUtil::Pool<MailBox::Mail> *			GETMAILPOOL() { return _clsMailPool; }
	NUtil::Pool<Trade::TradeItem> *			GETTRADEITEMPOOL() { return _clsTradeItemPool; }

	ServerAddrCCU	*		GETSERVERADDRCCU() { return _clsServerAddrCCU; }
	Memcached * GETMEMCACHED() { return _clsMemcached; }

	NUtil::Pool<Battle> * GETBATTLEPOOL() { return _pBattlePool; }

	int	GETCCU(const int channelNumber)
	{
		auto iter = _clsWorldTree.find(channelNumber);
		if(_clsWorldTree.end() == iter)
			return -1;

		return iter->second->GetCCU();
	}

	NUtil::Pool<Indexer> * 		GETINDEXERPOOL() { return _clsIndexerPool; }
	NUtil::Pool<IndexerPVP> * 	GETPVPINDEXPOOL() { return _clsPVPIndexerPool; }
	NUtil::Pool<IndexerPK> * 	GETPKINDEXPOOL() { return _clsPKIndexerPool; }
	PARTYROOMTREE &			GETPARTYROOMTREE() { return _clsPartyRoomTree; }
	PKROOMTREE &				GETPKROOMTREE() { return _clsPKRoomTree; }
	SPACEVECTOR &				GETPVPROOMVECTOR() { return _clsPVPRoomVector; }

	WORLDBOSSROOMTREE &					GETWORLDBOSSROOMTREE() 	{ return _clsWorldBossRoomTree; }
	WorldBossRank *						GETWORLDBOSSRANK() 		{ return _pWorldBossRank; }
	NUtil::Pool<IndexerWorldBoss> *		GETWORLDBOSSINDEXPOOL() 	{ return _pWorldBossIndexerPool; }

	WORLDBOSSROOMTREE &					GETWORLDBOSSROOMTREE_FRIEND() 	{ return _clsWorldBossRoomTree_Friend; }


	NUtil::Pool<IndexerNewPartyRoomV2> *		GETINDEXERNEWPARTYROOMV2POOL() { return _clsIndexerNewPartyRoomV2Pool; }
	PARTYROOMTREE_V2	&							GETPARTYROOMTREEV2() { return _clsPartyRoomTree_V2;}

private:
	NUtil::Pool_Exception<Space> *		_clsSpacePool;
	SPACETREE					_clsSpaceTree;
	SPACETREE					_clsWorldTree;
	// 신규 PVP Room 생성.
	SPACEVECTOR				_clsPVPRoomVector;

	// 신규 파티 기획에 따른 새로운 Room 생성....
	PARTYROOMTREE				_clsPartyRoomTree;

	PKROOMTREE					_clsPKRoomTree;

	NUtil::Pool<Monster> *	_clsMonsterPool;
	std::map<Monster*, int> _clsMonsterCounter;

	NUtil::Pool<Item> *		_clsItemPool;
	std::map<Item*, int>		_clsItemCounter;

	NUtil::Pool<QuestInfo::Quest> *	_clsQuestPool;
	NUtil::Pool<Party> *		_clsPartyPool;
	NUtil::Pool<MailBox::Mail> *			_clsMailPool;
	NUtil::Pool<Trade::TradeItem> *			_clsTradeItemPool;

	ServerAddrCCU	*				_clsServerAddrCCU;
	Memcached		*				_clsMemcached;

	NUtil::Pool<Battle> *		_pBattlePool;

	NUtil::Pool<Indexer> *		_clsIndexerPool;
	NUtil::Pool<IndexerPVP> *	_clsPVPIndexerPool;
	NUtil::Pool<IndexerPK> *	_clsPKIndexerPool;

	// world boss
	WORLDBOSSROOMTREE						_clsWorldBossRoomTree;
	WorldBossRank *						_pWorldBossRank;
	NUtil::Pool<IndexerWorldBoss> *		_pWorldBossIndexerPool;
	// world boss

	// world boss friend
	WORLDBOSSROOMTREE						_clsWorldBossRoomTree_Friend;
	// world boss


	NUtil::Pool<IndexerNewPartyRoomV2> *		_clsIndexerNewPartyRoomV2Pool;
	PARTYROOMTREE_V2								_clsPartyRoomTree_V2;
};

} /*NLogic*/ } /*ProjectZ*/

#endif /* STATIC_H_ */
